"""
object的特殊成员
"""


class MyClass:
    """
    MyClass describe
    """
    s_para = "para"

    def __init__(self, name, age):
        print("__init__ running")
        self.name = name
        self.age = age
        self.func4 = 4

    def __str__(self):
        return "str"

    def __repr__(self):
        """
        显示属性
        通常情况下，直接输出某个实例化对象，我们得到的信息只会是“类名+object at+内存地址”，对我们了解该实例化对象帮助不大。
        那么，想自定义输出实例化对象时的信息。
        就可以，通过重写类的 __repr__() 方法即可。
        事实上，当我们输出某个实例化对象时，其调用的就是该对象的 __repr__() 方法，输出的是该方法的返回值。
        :return:
        """
        return "{0}[name:{1}, age:{2}]".format(self.__class__.__name__, self.name, self.age)
        # return "repr"

    def __call__(self, *args, **kwargs):
        """
        对象后面加括号，触发执行，相当于函数调用
        构造方法的执行是由创建对象触发的，即：对象 = 类名()
        而对于 call 方法的执行是由对象后加括号触发的，即：对象() 或者 类()()
        :param args:
        :param kwargs:
        :return:
        """
        print("__call__ running: args:{0} kwargs:{1}".format(args, kwargs))
        return "call"

    def __del__(self):
        """
        析构方法，当对象被销毁时调用
        :return:
        """
        print("__del__ running")

    def func(self):
        print("func running")

    @staticmethod
    def func2(info):
        print("func running" + info)

    @classmethod
    def func3(cls):
        print("func running")


if __name__ == "__main__":
    cls = MyClass("zhangsan", 18)
    print(cls.__doc__)  # 类的描述信息(类的注释)
    print(cls.__module__)  # 表示当前操作的对象在那个模块
    print(cls.__dir__())  # 列出对象的所有属性（方法）名(包括类属性和staticMethod/classMethod修饰的方法)，与 dir(cls) 等价
    print(cls.__dict__)  # 查看对象内部所有属性名和属性值组成的字典(不包括类属性)
    print(cls.__class__)  # 表示当前操作的对象的类是什么
    print(cls)  # 如果有重写__str__ 则调用__str__ 否则调用cls.__repr__
    print(cls(1, 2, info="information"))  # 调用__call__

    # hasattr用来判断某个类实例对象是否包含指定名称的属性或方法(包括类属性和staticMethod/classMethod修饰的方法
    print(hasattr(cls, "func"))
    print(hasattr(cls, "f"))
    print(hasattr(cls, "func2"))
    print(hasattr(cls, "func3"))
    print(hasattr(cls, "s_para"))

    # getattr获取某个类实例对象中指定属性的值(包括类属性和staticMethod/classMethod修饰的方法)。
    # 如果不给默认值，找不到时就会抛出 AttributeError 错误
    print(getattr(cls, "name"))
    print(getattr(cls, "func"))
    print(getattr(cls, "func2", "no func2"))
    print(getattr(cls, "func3", "no func3"))
    print(getattr(cls, "s_para"))
    # print(getattr(cls, "f"))  # 会抛出AttributeError 错误

    # setattr修改类实例对象中的属性值。其次，它还可以实现为实例对象动态添加属性或者方法
    # 添加一个属性
    setattr(cls, "score", 100)
    print(cls.score)
    cls2 = MyClass("lisi", 19)

    def func4(*args, **kwargs):
        print("func4 running begin args:{0} kwargs:{1}".format(args, kwargs))
        print(cls2.name)
        print("func4 running end")
    # cls.name()  # 没设置该属性前调用会报错 'str' object is not callable
    # 将属性改成方法
    setattr(cls, "name", func4)
    cls.name(3, 4, info="test")

    # 检查类型的函数
    print(issubclass(MyClass, object))  # True
    print(isinstance(cls, MyClass))  # True
    print(isinstance(cls, object))  # True
    print(isinstance(MyClass, MyClass))  # False
